using Godot;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

public partial class Robot : Node2D
{
    [Signal]
    public delegate void OnMoveEndEventHandler(Robot robot);

    [Signal]
    public delegate void ActionEndEventHandler(Robot robot);

    [Signal]
    public delegate void OnRobotDestroyEventHandler(Robot robot);

    // [Signal]
    // public delegate void OnApplyDamage(Robot robot);

    // [Signal]
    // public delegate void OnTakeDamageEventHandler(Robot robot);

    #region 子组件
    Sprite2D icon;
    Area2D area2D;
    ShaderMaterial material;
    #endregion

    public RobotAIBehavior Behavior { get; set; }

    public bool IsDeath
    {
        get => Entity.CurrentHealth <= 0;
    }

    void GetChildComponents()
    {
        icon = GetNode<Sprite2D>("Icon");
        area2D = GetNode<Area2D>("Area2D");
        material = icon.Material.Duplicate() as ShaderMaterial;
        icon.Material = material;
    }
    public override void _Ready()
    {
        GetChildComponents();
    }

    public BaseAIComponent AIComponent { get; set; }

    public void RunAI()
    {
        if (AIComponent == null)
        {
            foreach (var item in GetChildren())
            {
                if (item is BaseAIComponent ai)
                {
                    AIComponent = ai;
                    break;
                }
            }
        }
        AIComponent.Run();
    }

    public RobotEntity Entity { get; private set; }
    public bool IsPlayer { get; private set; }
    public bool IsAlly { get; private set; }
    public bool IsEnemy { get => !IsPlayer && !IsAlly; }

    public PilotEntity Pilot => Entity.Pilot;

    public void InitData(RobotModel rmd, PilotModel pmd, int id, bool isPlayer, bool isAlly, int level = 1)
    {
        Entity = new RobotEntity(rmd, pmd, id, this)
        {
            Level = level
        };
        Entity.CurrentHealth = Entity.TotalHealth;
        Entity.CurrentSprite = Entity.TotalSprite;
        IsPlayer = isPlayer;
        IsAlly = isAlly;
        icon.Texture = GameUtil.GetRobotIcon(rmd.Icon, isPlayer, isAlly);
    }

    public Vector2 PrevPos;
    Tween tween;
    int oldZIndex;
    static readonly Vector2 offset = new Vector2(0, -10);
    readonly float moveSpeed = 0.11f;
    public void MoveByPath(List<MoveGrid> list)
    {
        PrevPos = Position;
        GameManager.Instance.LockInput();
        oldZIndex = ZIndex;
        ZIndex = 2;
        tween = CreateTween();
        tween.TweenProperty(icon, "position", icon.Position + offset, moveSpeed);
        foreach (var item in list)
        {
            tween.TweenProperty(this, "position", item.Position, moveSpeed);
        }
        var tarPos = list.Last().Position;
        tween.TweenProperty(icon, "position", icon.Position, moveSpeed);
        tween.Finished += OnTweenFinishedHandler;
    }

    public void UndoMove()
    {
        IsMoveEnd = false;
        Position = PrevPos;
    }

    private void OnTweenFinishedHandler()
    {
        IsMoveEnd = true;
        GameManager.Instance.AllowInput();
        ZIndex = oldZIndex;
        EmitSignal(SignalName.OnMoveEnd, this);
    }

    public bool IsMoveEnd { get; private set; }
    public bool IsActionEnd { get; private set; }

    public void SetRobotActionEnd(bool end = true)
    {
        IsMoveEnd = end;
        IsActionEnd = end;
        material.SetShaderParameter("percentage", end ? 0 : 1);
        if (end)
        {
            EmitSignal(SignalName.ActionEnd, this);
        }
    }

    public int DistanceTo(Robot robot, BaseMapManager map)
    {
        var p = map.LocalToMap(Position) - map.LocalToMap(robot.Position);
        return Mathf.Abs(p.X) + Mathf.Abs(p.Y);
    }

    public int MapDistanceTo(Robot robot)
    {
        var map = GameManager.Instance.LevelManager.MapManager;
        return DistanceTo(robot, map);
    }

    public List<WeaponEntity> GetWeaponForBeatBack(Robot tar)
    {
        return Entity.WeaponList.FindAll(v => v.RangeMax >= MapDistanceTo(tar));
    }

    internal WeaponEntity ChooseWeapon(Robot target)
    {
        // 取在射程内的武器
        var list = GetWeaponForBeatBack(target);
        if (list.Count > 0)
        {
            // 暂使用随机的一个
            var idx = new Random().Next(list.Count);
            return list[idx];
        }
        return null;
    }

    public int AddExp(int exp)
    {
        Entity.Exp += exp;
        while (Entity.Exp >= Entity.MaxExp)
        {
            Entity.Exp -= Entity.MaxExp;
            Entity.Level++;
        }
        return Entity.Level;
    }

    public int AttackTarget(Robot target, WeaponEntity weapon, bool isVitrual = false)
    {
        var dmg = Entity.TotalAttack;
        dmg += target.Entity.MoveType switch
        {
            RobotMoveType.AIR => weapon.AttackAir,
            RobotMoveType.OCEAN => weapon.AttackOcean,
            _ => weapon.AttackLand,
        };
        // 计算buff或者装备加成的伤害
        dmg = Mathf.RoundToInt(dmg * (1 + Entity.ExtraProperty.FinalDamageBonus));
        dmg = target.TakeDamage(dmg, isVitrual);
        return dmg;
    }

    private int TakeDamage(int dmg, bool isVitrual)
    {
        dmg -= Entity.TotalDefense;
        // 最终伤害在90%到110%之间波动
        dmg = (int)(dmg * GameUtil.Random(0.9f, 1.1f));

        // 计算buff或者装备减免的伤害
        dmg = Mathf.RoundToInt(dmg * (1 - Entity.ExtraProperty.FinalDamageReduction));

        dmg = Mathf.Max(dmg, 0);
        if (!isVitrual)
        {
            Entity.CurrentHealth -= dmg;
        }
        return dmg;
    }

    internal int GetMaxAttackRange()
    {
        var max = -1;
        foreach (var item in Entity.WeaponList)
        {
            if (item.RangeMax > max)
            {
                max = item.RangeMax;
            }
        }
        return max;
    }

    internal void TakeHeal(int amount)
    {
        Entity.CurrentHealth += amount;
        Entity.CurrentHealth = Mathf.Min(Entity.CurrentHealth, Entity.TotalHealth);
    }

    internal bool AllyToOther(Robot originRobot)
    {
        if ((IsPlayer || IsAlly) && (originRobot.IsPlayer || originRobot.IsAlly))
        {
            return true;
        }
        else if (IsEnemy && originRobot.IsEnemy)
        {
            return true;
        }
        return false;
    }

    public List<Robot> LoadedRobotList { get; } = new();
    public void MotherShipLoadRobot(Robot robot)
    {
        robot.Hide();
        LoadedRobotList.Add(robot);
        robot.SetRobotActionEnd();
    }

    internal void RecoverHealthOnMotherShip()
    {
        if (LoadedRobotList.Count > 0)
        {
            foreach (Robot robot in LoadedRobotList)
            {
                robot.TakeHeal(Mathf.CeilToInt(robot.Entity.TotalHealth * 0.3f));
            }
        }
    }

    internal void BuffRoundStartActions()
    {
        foreach (var item in Entity.BuffList)
        {
            item.RoundStart();
        }
        foreach (var item in Entity.BuffList)
        {
            if (item.IsDestroyed)
            {
                Entity.BuffList.Remove(item);
            }
        }
    }
}